package com.clear.honghefinance.biz.modules.assist_calculate_summary.service.impl;

import cn.hutool.extra.pinyin.PinyinUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clear.honghefinance.biz.modules.system.member.domain.vo.AdminDTO;
import com.clear.honghefinance.biz.modules.assist_calculate.domain.entity.AssistCalculateCate;
import com.clear.honghefinance.biz.modules.assist_calculate.service.IAssistCalculateCateService;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.converter.AssistCalculateSummaryConvertor;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.domain.dto.*;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.domain.entity.AssistCalculateSummary;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.domain.vo.GetAssistCalculateBaseVO;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.domain.vo.ListAssistCalculateBaseVO;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.domain.vo.ListAssistCalculateSummaryVO;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.handler.AssistCalculateHandler;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.handler.AssistCalculateHandlerFactory;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.mapper.AssistCalculateSummaryMapper;
import com.clear.honghefinance.biz.modules.assist_calculate_summary.service.IAssistCalculateSummaryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.clear.honghefinance.common.dto.entity.PageResult;
import com.clear.honghefinance.common.exception.BizException;
import com.clear.honghefinance.common.service.TokenService;
import com.clear.honghefinance.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 辅助核算汇总表（所有类别） 服务实现类
 * </p>
 *
 * @author 程序员clear
 * @since 2024-08-23
 */
@Service
@Slf4j
public class AssistCalculateSummaryServiceImpl extends ServiceImpl<AssistCalculateSummaryMapper, AssistCalculateSummary> implements IAssistCalculateSummaryService {

    @Resource
    private TokenService<AdminDTO> tokenService;
    @Resource
    private IAssistCalculateCateService assistCalculateCateService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private AssistCalculateHandlerFactory assistCalculateHandlerFactory;

    /**
     * 创建辅助核算
     *
     * @param dto
     * @return
     */
    @Override
    public <T extends CreateAssistCalculateBaseDTO> boolean create(T dto) {
        if (log.isInfoEnabled()) {
            log.info("创建辅助核算中，入参：{}", JSON.toJSONString(dto));
        }
        try {
            AssistCalculateSummary assistCalculateSummary = AssistCalculateSummaryConvertor.INSTANCE.convertDTOToEntity(dto);
            assistCalculateSummary.initDefault();
            assistCalculateSummary.setMemberId(tokenService.getThreadLocalUserId());
            assistCalculateSummary.setUpdateMemberId(tokenService.getThreadLocalUserId());
            assistCalculateSummary.setTenantId(tokenService.getThreadLocalTenantId());
            assistCalculateSummary.setMnemonicCode(PinyinUtil.getFirstLetter(dto.getName(), ""));
            //创建前检查
            createCheck(assistCalculateSummary);
            transactionTemplate.execute((transactionStatus) -> {
                this.save(assistCalculateSummary);
                dto.setAssistCalculateSummaryId(assistCalculateSummary.getId());
                // 根据类型选择对应的处理类
                AssistCalculateHandler assistCalculateHandleService = assistCalculateHandlerFactory.getHandler(assistCalculateSummary.getAssistCalculateCateCode());
                assistCalculateHandleService.create(dto);
                // 为辅助核算类别加1
                boolean flag = assistCalculateCateService.addUseCount(dto.getAssistCalculateCateId(), 1);
                if (!flag) {
                    throw new BizException("创建失败");
                }
                return true;
            });
            return true;
        } catch (Exception e) {
            log.error("创建辅助核算失败 {}", e.getMessage(), e);
            throw new BizException("创建辅助核算失败");
        }
    }

    /**
     * 修改辅助核算
     *
     * @param dto
     * @return
     */
    @Override
    public <T extends UpdateAssistCalculateBaseDTO> boolean update(T dto) {
        if (log.isInfoEnabled()) {
            log.info("修改辅助核算中，入参：{}", JSON.toJSONString(dto));
        }
        AssistCalculateSummary assistCalculate = getAssistCalculateById(dto.getId());
        if (assistCalculate == null) {
            throw new BizException("辅助核算不存在");
        }
        AssistCalculateSummary assistCalculate2 = getByAssistCalculateCateIdAndCode(assistCalculate.getAssistCalculateCateId(), dto.getCode());
        if (Objects.nonNull(assistCalculate2) && !Objects.equals(assistCalculate2.getId(), dto.getId())) {
            throw new BizException("辅助核算编码已存在");
        }
        LambdaUpdateWrapper<AssistCalculateSummary> lambdaUpdateWrapper = Wrappers.<AssistCalculateSummary>lambdaUpdate().eq(AssistCalculateSummary::getId, dto.getId()).eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId()).eq(AssistCalculateSummary::getDelFlag, false).set(AssistCalculateSummary::getCode, dto.getCode()).set(AssistCalculateSummary::getName, dto.getName()).set(AssistCalculateSummary::getMnemonicCode, PinyinUtil.getFirstLetter(dto.getName(), "")).set(AssistCalculateSummary::getUpdateTime, new Date()).set(AssistCalculateSummary::getUpdateMemberId, tokenService.getThreadLocalUserId()).set(AssistCalculateSummary::getNotes, dto.getNotes());

        transactionTemplate.execute((transactionStatus) -> {
            if (!this.update(lambdaUpdateWrapper)) {
                throw new BizException("修改辅助核算失败");
            }
            // 根据类型编码获取辅助核算Handler
            AssistCalculateHandler assistCalculateHandler = assistCalculateHandlerFactory.getHandler(assistCalculate.getAssistCalculateCateCode());
            if (!assistCalculateHandler.update(dto)) {
                throw new BizException("修改辅助核算失败");
            }
            return true;
        });
        return true;
    }

    /**
     * 查询辅助核算列表
     *
     * @param dto
     * @return
     */
    @Override
    public PageResult<ListAssistCalculateSummaryVO> list(ListAssistCalculateSummaryDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("查询辅助核算列表中，入参：{}", JSON.toJSONString(dto));
        }
        try {
            IPage<AssistCalculateSummary> page = new Page<>(dto.getPageNum(), dto.getPageSize());
            LambdaQueryWrapper<AssistCalculateSummary> lambdaQueryWrapper = Wrappers.<AssistCalculateSummary>lambdaQuery().eq(AssistCalculateSummary::getDelFlag, false).eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId()).eq(Objects.nonNull(dto.getAssistCalculateCateId()), AssistCalculateSummary::getAssistCalculateCateId, dto.getAssistCalculateCateId());
            if (StringUtils.isNotBlank(dto.getCodeOrName())) {
                lambdaQueryWrapper.eq(AssistCalculateSummary::getCode, dto.getCodeOrName()).or().like(AssistCalculateSummary::getName, dto.getCodeOrName());
            }
            this.page(page, lambdaQueryWrapper);

            List<AssistCalculateSummary> records = page.getRecords();
            List<ListAssistCalculateSummaryVO> listAssistCalculateSummaryVOS = AssistCalculateSummaryConvertor.INSTANCE
                    .convertEntityListToListAssistCalculateSummaryVOPageResult(records);

            PageResult<ListAssistCalculateSummaryVO> result = new PageResult<>();
            result.setList(listAssistCalculateSummaryVOS);
            result.setPageNum(dto.getPageNum());
            result.setPageSize(dto.getPageSize());
            result.setPages((int) page.getPages());
            result.setTotal((int) page.getTotal());
            return result;
        } catch (Exception e) {
            log.error("查询辅助核算列表失败 {}", e.getMessage(), e);
            throw new BizException("查询辅助核算列表失败");
        }
    }

    /**
     * 查询辅助核算列表
     *
     * @param dto
     * @param returnType
     */
    @Override
    public <R extends ListAssistCalculateBaseVO> PageResult<R> list(ListAssistCalculateDTO dto, Class<R> returnType) {
        if (log.isInfoEnabled()) {
            log.info("查询辅助核算列表中，入参：{}", JSON.toJSONString(dto));
        }
        IPage<AssistCalculateSummary> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<AssistCalculateSummary> lambdaQueryWrapper = Wrappers.<AssistCalculateSummary>lambdaQuery().eq(AssistCalculateSummary::getDelFlag, false).eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId()).eq(Objects.nonNull(dto.getAssistCalculateCateId()), AssistCalculateSummary::getAssistCalculateCateId, dto.getAssistCalculateCateId());
        if (StringUtils.isNotBlank(dto.getCodeOrName())) {
            lambdaQueryWrapper.eq(AssistCalculateSummary::getCode, dto.getCodeOrName()).or().like(AssistCalculateSummary::getName, dto.getCodeOrName());
        }
        this.page(page, lambdaQueryWrapper);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return new PageResult<>();
        }

        String cateCode = page.getRecords().get(0).getAssistCalculateCateCode();  // 辅助核算类别编码

        List<Long> ids = page.getRecords().stream().map(AssistCalculateSummary::getId).collect(Collectors.toList());

        AssistCalculateHandler assistCalculateHandler = assistCalculateHandlerFactory.getHandler(cateCode);

        List<? extends ListAssistCalculateBaseVO> assistCalculateList = assistCalculateHandler.listByAssistCalculateSummaryIds(ids);

        if (CollectionUtils.isEmpty(assistCalculateList)) {
            throw new BizException("查询辅助核算列表异常");
        }
        List<R> data = convertToList(assistCalculateList, returnType);
        data.forEach(item -> {
            page.getRecords().stream().filter(p -> p.getId().equals(item.getAssistCalculateSummaryId())).findFirst().ifPresent(i -> {
                item.setId(i.getId());
                item.setCode(i.getCode());
                item.setName(i.getName());
                item.setNotes(i.getNotes());
                item.setMnemonicCode(i.getMnemonicCode());
                item.setDisable(i.getDisable());
            });

        });
        PageResult<R> result = new PageResult<>();
        result.setPageNum(dto.getPageNum());
        result.setPageSize(dto.getPageSize());
        result.setPages((int) page.getPages());
        result.setTotal((int) page.getTotal());
        result.setList(data);

        return result;
    }

    /**
     * 查询辅助核算详情
     *
     * @param id
     * @param returnType
     * @return
     */
    @Override
    public <R extends GetAssistCalculateBaseVO> R getDetailById(Long id, Class<R> returnType) {
        if (log.isInfoEnabled()) {
            log.info("查看辅助核算明细中，入参：id={}，returnTypes={}", id, returnType);
        }
        try {
            AssistCalculateSummary assistCalculateById = this.getAssistCalculateById(id);
            if (Objects.isNull(assistCalculateById)) {
                throw new BizException("辅助核算不存在");
            }

            AssistCalculateHandler assistCalculateHandler = assistCalculateHandlerFactory
                    .getHandler(assistCalculateById.getAssistCalculateCateCode());

            R result = returnType.cast(assistCalculateHandler.getDetailById(id));
            if (Objects.isNull(result)) {
                throw new BizException("辅助核算不存在");
            }
            result.setId(assistCalculateById.getId());
            result.setCode(assistCalculateById.getCode());
            result.setName(assistCalculateById.getName());
            result.setNotes(assistCalculateById.getNotes());
            result.setMnemonicCode(assistCalculateById.getMnemonicCode());

            return result;
        } catch (
                Exception e) {
            log.error("查看辅助核算明细异常 {}", e.getMessage(), e);
            throw new BizException("查看辅助核算明细异常");
        }

    }

    /**
     * 禁用或启用辅助核算
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean updateDisable(UpdateAssistCalculateDisableDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("{}辅助核算中，入参：{}", dto.getDisable() ? "禁用" : "启用", JSON.toJSONString(dto));
        }
        try {
            LambdaUpdateWrapper<AssistCalculateSummary> lambdaUpdateWrapper = Wrappers.<AssistCalculateSummary>lambdaUpdate().eq(AssistCalculateSummary::getId, dto.getId()).eq(AssistCalculateSummary::getDisable, !dto.getDisable())  // 状态与传入的不同才会改变
                    // 只改变未删除的禁用状态
                    .eq(AssistCalculateSummary::getDelFlag, false).eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId()).set(AssistCalculateSummary::getDisable, dto.getDisable()).set(AssistCalculateSummary::getUpdateMemberId, tokenService.getThreadLocalUserId()).set(AssistCalculateSummary::getUpdateTime, DateUtil.getSystemTime());

            if (this.update(lambdaUpdateWrapper)) {
                return true;
            } else {
                throw new BizException(String.format("%s辅助核算异常", dto.getDisable() ? "禁用" : "启用"));
            }
        } catch (Exception e) {
            log.error("{}辅助核算异常 {}", dto.getDisable() ? "禁用" : "启用", e.getMessage(), e);
            throw new BizException(String.format("%s辅助核算异常", dto.getDisable() ? "禁用" : "启用"));
        }

    }

    /**
     * 删除辅助核算
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean delete(DeleteAssistCalculateDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("删除辅助核算中，入参：{}", JSON.toJSONString(dto));
        }
        AssistCalculateSummary assistCalculateById = getAssistCalculateById(dto.getId());
        if (assistCalculateById == null) {
            throw new BizException("辅助核算不存在");
        }

        try {
            LambdaUpdateWrapper<AssistCalculateSummary> lambdaUpdateWrapper = Wrappers.<AssistCalculateSummary>lambdaUpdate()
                    .eq(AssistCalculateSummary::getId, dto.getId())
                    .eq(AssistCalculateSummary::getDelFlag, false)
                    // 目前只能删除自己的辅助核算
                    .eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId())
                    .set(AssistCalculateSummary::getDelFlag, true)
                    .set(AssistCalculateSummary::getUpdateMemberId, tokenService.getThreadLocalUserId())
                    .set(AssistCalculateSummary::getUpdateTime, DateUtil.getSystemTime());

            transactionTemplate.execute(transactionTemplate -> {
                if (!this.update(lambdaUpdateWrapper)) {
                    if (log.isInfoEnabled()) {
                        log.info("删除辅助核算异常，入参：{}", JSON.toJSONString(dto));
                    }
                    throw new BizException("删除辅助核算异常");
                }
                if (!assistCalculateCateService.deductUseCount(assistCalculateById.getAssistCalculateCateId(), 1)) {
                    if (log.isInfoEnabled()) {
                        log.info("删除辅助核算异常，入参：{}", JSON.toJSONString(dto));
                    }
                    throw new BizException("删除辅助核算异常");
                }
                // todo 考虑删除对应明细表

                return true;
            });

            return true;
        } catch (Exception e) {
            log.error("删除辅助核算异常 {}", e.getMessage(), e);
            throw new BizException("删除辅助核算异常");
        }
    }


    /**
     * 将辅助核算列表转换为指定类型的列表
     *
     * @param assistCalculateList
     * @param returnType
     * @param <R>
     * @return
     */
    private <R extends ListAssistCalculateBaseVO> List<R> convertToList(List<? extends ListAssistCalculateBaseVO> assistCalculateList, Class<R> returnType) {
        List<R> result = new ArrayList<>();
        for (Object assistCalculate : assistCalculateList) {
            // 进行转换并添加到结果列表中
            result.add(returnType.cast(assistCalculate));
        }
        return result;
    }

    /**
     * 根据id查询辅助核算信息
     *
     * @param id
     * @return
     */
    private AssistCalculateSummary getAssistCalculateById(Long id) {
        LambdaQueryWrapper<AssistCalculateSummary> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AssistCalculateSummary::getId, id).eq(AssistCalculateSummary::getDelFlag, false).eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId());
        return this.getOne(wrapper);
    }

    /**
     * 创建辅助核算前校验
     *
     * @param dto
     */
    private void createCheck(AssistCalculateSummary dto) {
        AssistCalculateCate assistCalculateCate = assistCalculateCateService.getByCategoryId(dto.getAssistCalculateCateId());
        if (assistCalculateCate == null) {
            throw new BizException("辅助核算类别编码不存在");
        }
        AssistCalculateSummary assistCalculateSummary = this.getByAssistCalculateCateIdAndCode(dto.getAssistCalculateCateId(), dto.getCode());
        if (Objects.nonNull(assistCalculateSummary) && !Objects.equals(assistCalculateSummary.getId(), dto.getId())) {
            throw new BizException("辅助核算编码已存在");
        }
        dto.setAssistCalculateCateCode(assistCalculateCate.getCode());
    }

    /**
     * 根据 辅助核算类别id和编码 查询辅助核算类别信息
     *
     * @param assistCalculateCateId 辅助核算类别id
     * @param code                  编码
     * @return
     */
    private AssistCalculateSummary getByAssistCalculateCateIdAndCode(long assistCalculateCateId, String code) {
        return this.getOne(Wrappers.<AssistCalculateSummary>lambdaQuery().eq(AssistCalculateSummary::getAssistCalculateCateId, assistCalculateCateId).eq(AssistCalculateSummary::getCode, code).eq(AssistCalculateSummary::getDelFlag, false).eq(AssistCalculateSummary::getTenantId, tokenService.getThreadLocalTenantId()));
    }
}
